#pragma once
#include <sys/stat.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include "Directory.h"
#include "File.h"
#include "Cache.h"
#include <cstring>
#include <stdlib.h>
#define MAX_INODES 192752 //192752 is the maximum number of inodes allowed in our system
#define FREE_LIST_HEAD 6168064 //location of the head of the data free list in the superblock
#define FREE_LIST_COUNT 6168068 //location of the number of free data blocks in the superblock 
#define INODE_FREE_LIST_HEAD 6168072 //location of the head of the inode free list in the superblock
#define INODE_FREE_LIST_COUNT 6168076 //location of the number of free inodes in the superblock 
#define MODTIME 4 //look 4 bytes into the inode for the modifcation time of the file
#define NUMLINKS 12 //look 12 bytes into the inode for the number of links
#define FILESIZE 16 //look 16 bytes into the inode for the file size
#define ISDIR 20 //look 20 bytes into the inode for the directory bit
#define BLOCKCONTENTSIZE 504 //each block can have at most 504 bytes of data
#define BLANKSCRATCH 6168080 //the beginning of the superblock  
#define INODE_SIZE 32 //the size of an inode
#define BLOCK_SIZE 512 //the size of a data block
#define MAXFILENAMESIZE 32 // the max length of a filename
#define THE_END 123456789 //our special pointer to the end that flags when we overwrite the file system
//#define uint unsigned int 
#define MINFREEBLOCKS 17 //we send warnings when we have fewer than 17 blocks free
typedef unsigned int uint;

class FileSystem {
 public:
	std::fstream fs;		// the 100 MB file representing the file system
	FILE *fp;			// the file pouinter to the 100 MB file representing the file system
	std::string filesystemname;	// name of 100 MB file
	std::set <int> unusedFileDesc; // the unused file descriptors 
	bool redirect; //a flag for redirection
	int maxFileDesc; //the max file descriptor that is currently in use
	Directory *root; //a pointer to the root directory 
	Directory *currentDirectory; //a pointer to the current directory 
	ostringstream output;	//read buffer
	istringstream input; //write buffer
	bool usecache; // a flag to not use the cache during the creation of the file system 
	bool defaultCacheBool; // flag to use the cache
	std::map <uint, File *> openFiles; // File descriptors, pointer to the file class

	Cache cache; //our cache
	/**************METHODS******************/

	/******************
    GET
	Parameters: size of space to read, location of space to read, a buffer uinto which you will be reading
    This function reads the designated seciotn uinto a buffer for later use
	*****************/
	void get (uint size, uint loc, void *buffer); 
	
	/******************
	PUT
	Parameters: the size of the space to write, location of the space to write, a buffer uinto which you will be writing
	This function writes the designated section using a buffer
	*****************/
	void put(uint size, uint loc, void *buffer); 
	
	/*******************
	FILE SYSTEM CONSTRUCTOR
	The boolean value says whether or not the filesystem will have a cache
	******************/
	FileSystem(bool defCache);
	
	/******************
	INIT FILE SYSTEM
	Parameters: 
	This function:
	1) writes the entire inital file system file
	2) The only used inode and block you have are for the root
	3) the rest of the file is completely empty, and mainly written with the character '0'
	*****************/
	void InitFileSystem(std::string fn); 

	/******************
	OPEN FILE
	Parameters: string filename, R/W flag
	This function:
	Checks to see if the file exists, if not: it creates the file
	If the file exists, it opens it for the flag specified
	It add this as an entry in our "open files" data structure in memory
	*****************/
	File* OpenFile(string filename, string flag); 

	/******************
	CLOSE FILE
	Parameters: 
	This function:
	Checks to see if the file is open, if it does, it closes it
	It deletes this as an entry in our "open files" data structure in memory
	*****************/
	void CloseFile(int fd); 
	
	/******************
	WRITE BLANK BLOCK
	Parameters: the address for the blank block, address for the next block, address for the previous block i
	Startting at the given address, this function:
	1) writes the previous address
	2) writes the next address
	3) fills up the remainder of the block with 504 characters of '0'
	 *****************/
	void WriteBlankBlock(uint address, uint next, uint previous);

	/******************
	WRITE BLANK INODE
	Parameters: the address of the blank inode, and the address of the next inode
	Starting at the given address, this function:
	1) writes the address of the next blank inode
	2) fills up the reminder of the block with 28 characters of '0'
	 *****************/
	void WriteBlankInode(uint address, uint next); 
	
	/******************
	WRITE BLANK SCRATCH
	Parameters: None
	This function writes 512 characters of '0' in the scratch space
	 *****************/
	void WriteBlankScratch();

	/******************
	SET INODE DATA: ADDRESS
	Parameters: the inode address, and the block address to update
	This function writes the new block address in the correct location of the inode
	 *****************/
	void SetInodeData_Address(uint inodeAddr, uint blockAddr);	
	
	/******************
	GET INODE DATA: ADDRESS
	Parameters: the inode address
	This function reads the new block address in the correct location of the inode
	 *****************/
	uint GetInodeData_Address(uint inodeAddr);	

	/******************
	SET INODE DATA: MOD_TIME
	Parameters: the inode address, and the time to update
	This function writes the new time in the correct location of the inode
	 *****************/
	void SetInodeData_ModTime(uint inodeAddr, time_t modTime);	
	
	/******************
	GET INODE DATA: MOD_TIME
	Parameters: the inode address
	This function readss the new time in the correct location of the inode
	 *****************/
	time_t GetInodeData_ModTime(uint inodeAddr);	
	
	/******************
	SET INODE DATA: NUMBER OF LINKS
	Parameters: the inode address, and the new number of links to update
	This function writes the new number of links in the correct location of the inode
	 *****************/
	void SetInodeData_NumLinks(uint inodeAddr, uint numLinks);	
	
	/******************
	GET INODE DATA: NUMBER OF LINKS
	Parameters: the inode address
	This function reads the new number of links in the correct location of the inode
	 *****************/
	uint GetInodeData_NumLinks(uint inodeAddr);	
	
	/******************
	SET INODE DATA: FILE SIZE
	Parameters: the inode address, and the new file size to update
	This function writes the new file size in the correct location of the inode
	 *****************/
	void SetInodeData_FileSize(uint inodeAddr, uint size);	
	
	/******************
	GET INODE DATA: FILE SIZE
	Parameters: the inode address
	This function reads the new file size in the correct location of the inode
	 *****************/
	uint GetInodeData_FileSize(uint inodeAddr);	

	/******************
	SET INODE DATA: DirBit
	Parameters: the inode address, and the DirBit to update
	This function writes the new DirBit in the correct location of the inode
	 *****************/
	void SetInodeData_DirBit(uint inodeAddr, char bit);	
	
	/******************
	GET INODE DATA: DirBit
	Parameters: the inode address
	This function reads the new DirBit in the correct location of the inode
	 *****************/
	char GetInodeData_DirBit(uint inodeAddr);	
	
	/******************
	PROCESS COMMAND
	This funciton processes the various commands which our shell recgonizes
	 *****************/
	uint ProcessCommand(std::string command);

	/******************
	MAKES A BLOCK FREE
	*****************/
	void MakeBlockFree(uint addressOfBlock); 
	
	/******************
	MAKES A INODE FREE
	 *****************/
	void MakeInodeFree(uint addressOfInode);
	
	/******************
	ALLOCATE INODE
	Parameters: none
	This funciton: 1) finds the head of the free list, decremins the number of free inodes
	2) update the head of the free list and return old head of the free list
	 *****************/
	uint AllocateInode(); 
	
	/******************
	ALLOCATE BLOCK
	Parameters: none
	This function: 1) takes a block off of the free list, and returns the block addres for you to now use
	 *****************/
	uint AllocateBlock();	
	
	/******************
	CREATES A FILE
	 *****************/
	uint CreateFile(std::string filename, char isdir);
	
	/******************
	NUMBER OF FREE BLOCKS
	Parameters: none
	Read from scratch space, and return
	*****************/
	uint NumberOfFreeBlocks(); 
	
	/******************
	NUMBER OF FREE INODES
	Parameters: none
	Read from scratch space, and return
	 *****************/
	uint NumberOfFreeInodes(); 
		
	/******************
	FIND ASSOC INODE ADDRESS
	Parameters: Current directory inode address, and the filename
	This funciton will:
	1) look in the directory entry specified for the filename
	2) return the filename's associated inode address, -1 if it is not there
	*****************/
	uint FindAssocInodeAddress(uint curDirInodeAddress, string fileName); 	
	
	/******************
	WRITE TO DIR ENTRY
	Parameters: directory inode address, the file name, and the file's inode address
	This Function will
	1) add the filename to the directory specified
	2) update the number of links to the inode
	*****************/
	void WriteToDirEntry(uint dirInodeAddr, string filename, uint fileInodeAddr);  
	
	/******************
	FIND PARENT INODE
	Parameters: file name
	This function will: return the inode of the parent contain the file name
	*****************/
	uint FindParentInode(string filename); 
	
	/******************
	FIND FILE INODE
	Parameters: filename
	This function will:
	1) return the inode address of the file, or -1 if DNE
	 *****************/
	uint FindFileInode(string filename); 

	/******************
	HEAD OF THE INODE FREE LIST
	This function will:
	RETURN THE HEAD OF INODE FREE LIST FROM THE SUPERBLOCK
	 *****************/
	uint HeadOfInodeFreeList();
	
	/******************
	HEAD OF FREE LIST
	This function will:
	RETURNS THE HEAD OF DATA BLOCK FREE LIST (BLOCK) FROM THE SUPERBLOCK
	 *****************/
	uint HeadOfFreeList();
  
	/******************
	UPDATE HEAD OF FREE LIST
	Parameters: address of the new head
	This function will:
	UPDATES THE HEAD OF FREE DATA LIST IN THE SUPERBLOCK
	 *****************/
	void UpdateHeadOfFreeList(uint address);
	
	/******************
	UPDATE NUMBER OF FREE BLOCKS
	Parameters: the new number of free blocks
	This function will:
	UPDATE NUMBER OF FREE BLOCKS IN THE SUPERBLOCK
	*****************/
	void UpdateNumberOfFreeBlocks(uint number);
	
	/******************
	UPDATE HEAD OF INODE FREE LIST
	Parameters: address of the inode that is now the head of the free list
	This function will:
	UPDATE HEAD OF INODE FREE LIST IN THE SUPERBLOCK
	*****************/
	void UpdateHeadOfInodeFreeList(uint address);
	
	/******************
	UPDATE NUMBER OF FREE INODES
	Parameters: new number of free inodes
	This function will:
	UPDATE NUMBER OF FREE INODES IN THE SUPERBLOCK
	*****************/
	void UpdateNumberOfFreeInodes(uint number);
	
	/******************
	REMOVE FILE FROM DIR ENTRY
	Parameters: inode of directory inode, the name of the file, the address of the file's inode
	This function will:
	REMOVE FILE FROM DIRECTORY
	 *****************/
	void RemoveFileFromDirEntry(uint dirInode, string filename, uint inodeAdd);

	/******************
	WRITE
	Parameters: file descriptor, buffer
	This function will:
	write the buffer to the data block(s) associated with the file descriptor fd
	****************/
	void Write(int fd, string buff);
	
	/******************
	SEEK FILE
	Parameters: file descriptor, offset
	This function will:
	seek to the offset in the data block of the file associated with the descriptor FD
	****************/
	void SeekFile(uint fd, uint offset);
	
	/******************
	READ
	Parameters: 
	This function will:
	Read "size" number of bytes from the data block associated with file descriptor fd
	It will return the information read from the data block in a buffer
	****************/
	char* Read(uint fd, uint size);
	
	/******************
	LIST FILES
	Parameters: 
	This function will:
	list all of the files in the current directory (EXCLUDING . AND ..)
	****************/
	void ListFiles();
	
	/******************
	PRINT INODE INFORMATION
	Parameters: 
	This function will:
	for the given filename, this funciton will print the following inode information:
	Directory bit
	number of links
	size of the file
	number of blocks
	last time modified
	****************/
	void PrintInodeInfo(string filename);
	
	/******************
	CAT FILE
	Parameters: 
	This function will:
	Print the contents of the file (from its data blocks) to the shell
	****************/
	void CatFile(string filename);
	
	/******************
	IMPORT FILE
	Parameters: source filename, destination filename
	This function will:
	Access a file in the working directory of the machine
	Create a dest file in our file system with the contents of the file src
	****************/
	void ImportFile(string src, string dest);
	
	/******************
	EXPORT FILE 
	Parameters: source filename, dest filename
	This function will:
	Check to see if the source file exists in NKD file system
	If it does, it will create a file in the current directory of the machine
	****************/
	void ExportFile(string src, string dest);
	
	/******************
	UNLINK
	Parameters: filename, directory bool
	The boolean variable flags if it is permitble to treat "filename" as a directory
	This function will:
	Unline the filename, and delete it if the number of links is at zero
	****************/
	void unlink(string filename, bool);
	
	/******************
	Tree
	Parameters: current directory, and number of spaces to print
	This function will:
	Print the contents of the current directory, and make a recursive call if the filename of interest is a directory
	****************/
	void Tree(uint curDirInode, string printSpaces);
	
	/******************
	Shell
	Parameters: input filename, output filename
	This function will:
	 Enable redirection for our system 
	****************/
	void Shell(string infile, string outfile);

	/******************
	DE-CONSTRUCTOR! 
	 *****************/
	~FileSystem();
	
};
